package org.foreningsgatan.modules.glcore.view;


import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.HashSet;
import java.util.Set;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import javax.swing.JFrame;

import org.foreningsgatan.modules.glcore.graphics.GLGraphics;
import org.foreningsgatan.modules.glcore.graphics.TextureManager;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.FPSAnimator;

/**
 * Copyright (c) 2008 Joakim Back
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 * @author Joakim Back
 * 
 */
public abstract class GLApplication implements GLEventListener, KeyListener {
	private static long sCurrentFrame;
	
	private GLU glu = new GLU();

	private GLGraphics graphics;
	
	private Set<Integer> keys;
	
	private TextureManager textureManager;
	
	protected int width;
	
	protected int height;

	public GLApplication(String title, int width, int height, int fps) {
		this.width = width;
		this.height = height;
		
		GLCapabilities capabilities = new GLCapabilities();
		GLCanvas canvas = new GLCanvas(capabilities);
		canvas.setSize(width, height);
		canvas.addGLEventListener(this);
		canvas.addKeyListener(this);
		final Animator animator = new FPSAnimator(canvas, fps, true);
	
		JFrame frame = new JFrame(title);
		frame.getContentPane().add(canvas);
		frame.pack();
		frame.addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				animator.stop();
				System.exit(0);
			}
		});
		
		keys = new HashSet<Integer>();

		textureManager = new TextureManager();
		graphics = new GLGraphics(width, height);
		graphics.setTextureManager(textureManager);
		animator.start();
		
		sCurrentFrame = 0;
		
		frame.setVisible(true);
	}
	
	public void init(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		gl.glDisable(GL.GL_DEPTH_TEST);
		gl.setSwapInterval(0);
		
		/* Load Application Textures */
		
		loadTextures(textureManager);
		
		try {
			init();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void display(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
		gl.glClear(GL.GL_COLOR_BUFFER_BIT);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluOrtho2D(0, drawable.getWidth(), 0, drawable.getHeight());
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
		
		/* Prepare GLGraphics */

		graphics.setGL(gl);
		
		/* Render Application */
		
		sCurrentFrame++;
		
		render(graphics);
		afterRender();
	}
	
	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
			boolean deviceChanged) {
		/* Ignore */
	}
	
	public void reshape(GLAutoDrawable drawable, int x, int y, int width,
			int height) {
		/* Ignore */
	}
	
	abstract protected void init() throws Exception;
	
	abstract protected void loadTextures(TextureManager tm);

	abstract protected void render(GLGraphics g);
	
	protected void afterRender() {}
	
	public void keyPressed(KeyEvent e) {
		keys.add(new Integer(e.getKeyCode()));
	}
	
	public void keyReleased(KeyEvent e) {
		keys.remove(new Integer(e.getKeyCode()));
	}
	
	public boolean isKeyPressed(int keyCode) {
		return keys.contains(keyCode);
	}
	
	public void keyTyped(KeyEvent e) {
		/* Ignore */
	}
	
	public static long getCurrentFrame() {
		return sCurrentFrame;
	}
}
